﻿//    Copyright 2010,2011 Himanshu Vasishth
//   
//    This file is part of SecretVault.
//
//    SecretVault is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    SecretVault is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with SecretVault.  If not, see <http://www.gnu.org/licenses/>.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Common;
using System.Diagnostics;

namespace PasswordLocker.Controls
{
    /// <summary>
    /// Filters mouse move messages and fires MouseEnter or MouseLeave events
    /// </summary>
    public class MouseMessagesFilter : IMessageFilter
    {
        private const string LoggingComponent = "MouseMessageFilter";
        private MouseMessagesFilter()
        {

        }

        private Dictionary<Control, ControlState> controlsToMonitor =
                new Dictionary<Control, ControlState>();

        private static MouseMessagesFilter instance;
        public static MouseMessagesFilter Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new MouseMessagesFilter();
                    Application.AddMessageFilter(instance);
                }
                return instance;
            }
        }

        /// <summary>
        /// Starts monitoring mouse move for the given control. MouseEnter and
        /// MouseLeave are fired when mouse enters or leaves the control. Mouse is
        /// considered to be inside the control as
        /// </summary>
        public void WatchControl(Control control,
                                 EventHandler mouseEnter,
                                 EventHandler mouseLeave)
        {
            if (control != null &&
                !controlsToMonitor.ContainsKey(control))
            {
                controlsToMonitor[control] = new ControlState
                                                 {
                                                     MouseEnter = mouseEnter,
                                                     MouseLeave = mouseLeave,
                                                     MouseOnControl = false
                                                 };
            }
        }

        public void StopWatchingControl(Control control)
        {
            if (control != null &&
                !controlsToMonitor.ContainsKey(control))
            {
                controlsToMonitor.Remove(control);
            }
        }

        #region IMessageFilter Members

        public bool PreFilterMessage(ref Message m)
        {
            // get the position of the mouse
            var mousePosition = Cursor.Position;

            foreach (var control in controlsToMonitor.Keys)
            {
                if (control.IsDisposed)
                {
                    continue;
                }

                var positionOnControl = control.PointToClient(mousePosition);
                if (control.Width > positionOnControl.X &&
                    0 <= positionOnControl.X &&
                    control.Height > positionOnControl.Y &&
                    0 <= positionOnControl.Y)
                {
                    // if the mouse was not on this control previously then fire mouse enter
                    if (!controlsToMonitor[control].MouseOnControl &&
                        controlsToMonitor[control].MouseEnter != null)
                    {
                        controlsToMonitor[control].MouseOnControl = true;
                        controlsToMonitor[control].MouseEnter(control, new EventArgs());
                    }
                }
                else
                {
                    // if the mouse was on this control previously then fire mouse leave
                    if (controlsToMonitor[control].MouseOnControl &&
                        controlsToMonitor[control].MouseLeave != null)
                    {
                        controlsToMonitor[control].MouseOnControl = false;
                        controlsToMonitor[control].MouseLeave(control, new EventArgs());
                    }
                }
            }

            // No messages are filtered.
            //
            return false;
        }

        #endregion

        /// <summary>
        /// Struct used to keep track of listeners and current mouse state for a control
        /// </summary>
        private class ControlState
        {
            public bool MouseOnControl;
            public EventHandler MouseEnter;
            public EventHandler MouseLeave;
        }
    }

}
